import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.type.TypeFactory;

import be.derycke.pieter.com.COMException;
import jmtp.PortableDevice;
import jmtp.PortableDeviceFolderObject;
import jmtp.PortableDeviceManagerImplWin32;
import jmtp.PortableDeviceObject;
import jmtp.PortableDeviceStorageObject;
import jmtp.PortableDeviceToHostImpl32;

//https://github.com/ultrah/jMTPe
public class Bilibili {

	static boolean move = false;
	/**
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {

		move = false;
		//getList(0, "a.txt");
		copy(0, "b.txt", "Z:\\bilibili");
		
	}

	static PortableDeviceFolderObject getBilibili(PortableDeviceStorageObject root) {
		PortableDeviceFolderObject Android = (PortableDeviceFolderObject) findFile(root, "Android");
		PortableDeviceFolderObject data = (PortableDeviceFolderObject) findFile(Android, "data");
		PortableDeviceFolderObject bili = (PortableDeviceFolderObject) findFile(data, "tv.danmaku.bili");
		PortableDeviceFolderObject download = (PortableDeviceFolderObject) findFile(bili, "download");
		return download;
	}

	public static void getInfo(String aid) {
		HttpURLConnection con = null;

		BufferedReader buffer = null;

		try {
			URL url = new URL("https://api.bilibili.com/x/web-interface/view?aid=" + aid);
			con = (HttpURLConnection) url.openConnection();
			con.setRequestMethod("GET");
			con.setDoInput(true);
			con.setUseCaches(false);

			InputStream inputStream = con.getInputStream();

			String line;
			buffer = new BufferedReader(new InputStreamReader(inputStream, "utf-8"));

			FileOutputStream fos = new FileOutputStream("info.json");
			OutputStreamWriter osw = new OutputStreamWriter(fos, "utf-8");

			while ((line = buffer.readLine()) != null) {

				osw.write(line);
				osw.flush();

			}
			
			fos.close();
			
			Thread.sleep(1000);

		} catch (Throwable e) {
			e.printStackTrace();
		}
	}

	public static String cleanName(String name){
		name = name.replace("\\", "");
		name = name.replace("/", "");
		name = name.replace(":", "");
		name = name.replace("*", "×");
		name = name.replace("\"", "“");
		name = name.replace("-", "");
		name = name.replace("?", "？");
		name = name.replace("|", "｜");
		name = name.replace("...", "…");
		name = name.replace("", " ");
		name = name.replace("<", "《");
		name = name.replace(">", "》");
		name = name.replace("㊤", "（上）");
		name = name.replace("㊦", "（下）");
				
		name = name.trim();
		return name;
	}
	
	public static void getList(int dev, String fn) throws Exception {

		PortableDeviceStorageObject root = getRoot(dev);
		if(root==null) {
			System.out.print("root == null;");
			return;
		}
		PortableDeviceFolderObject bilibili = getBilibili(root);
		PortableDeviceObject[] objs = bilibili.getChildObjects();

		ArrayList<Video> list = new ArrayList<Video>();

		Comparator<Video> comparatorName = new Comparator<Video>() {
			@Override
			public int compare(Video a, Video b) {
				return a.title.compareTo(b.title);
			}
		};

		Comparator<Video> comparatorSize = new Comparator<Video>() {
			@Override
			public int compare(Video a, Video b) {
				return (a.size > b.size) ? -1 : 1;
			}
		};

		Comparator<Video> comparatorDate = new Comparator<Video>() {
			@Override
			public int compare(Video a, Video b) {
				return (a.date > b.date) ? -1 : 1;
			}
		};

		for (PortableDeviceObject item : objs) {
			System.out.println(item.getOriginalFileName());

			PortableDeviceObject[] all = getList(item);
			if (all == null)
				continue;
			boolean first = true;
			Video video = new Video();
			video.size = 0;
			for (PortableDeviceObject item2 : all) {
				{
					PortableDeviceObject entry = findFile(item2, "entry.json");

					try {
						deleteFile("entry.json");
						host.copyFromPortableDeviceToHost(entry.getID(), ".", device);
					} catch (COMException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				{
					PortableDeviceObject infoObj = findFile(item2, "info.json");

					Entry entry = readJson("entry.json", Entry.class);

					if (infoObj == null) {
						long aid;
						if (entry.avid == 0) {
							aid = entry.ep.av_id;
						} else {
							aid = entry.avid;
						}
						getInfo("" + aid);

						File file = new File("info.json");
						((PortableDeviceFolderObject) item2).addAudioObject(file, "--", "--", new BigInteger("0"));
						
						infoObj = findFile(item2, "info.json");
					}
					
					try {
						deleteFile("info.json");
						host.copyFromPortableDeviceToHost(infoObj.getID(), ".", device);
					} catch (COMException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					
					Info info = readJson("info.json", Info.class);

					if (first) {
						first = false;
						video.id = item.getOriginalFileName();
						video.title = cleanName(entry.title);
						video.owner_id = entry.owner_id;
						if (!"啥都木有".equals(info.message) && !"稿件不可见".equals(info.message)) {
							video.owner = info.data.owner.name;
						} else {
							video.owner = "？";
						}
						video.date = entry.time_update_stamp;
					}
					video.size += entry.downloaded_bytes;
				}
			}

			list.add(video);
		}
		Collections.sort(list, comparatorDate);//comparatorSize   comparatorDate
		

		FileOutputStream fos = new FileOutputStream(fn);
		OutputStreamWriter osw = new OutputStreamWriter(fos);

		for (Video item : list) {

			osw.write(item.id + "," + (item.size / 1024 / 1024) + "," + item.title + "," + item.owner_id + "," + item.owner + "\r\n");
			osw.flush();
		}

		fos.close();
	}

	static PortableDevice device;

	static PortableDeviceStorageObject getRoot(int idx) {

		PortableDeviceManagerImplWin32 manager = new PortableDeviceManagerImplWin32();
		for (PortableDevice device : manager.getDevices().values()) {
			device.open();
			Bilibili.device = device;
			PortableDeviceObject[] roots = device.getRootObjects();
			if (roots.length > idx) {
				for (int i = 0; i < roots.length; i++) {
					if (i == idx) {
						PortableDeviceStorageObject root = (PortableDeviceStorageObject) roots[i];
						return root;
					}
				}
			}
		}
		return null;
	}

	static PortableDeviceObject findFile(PortableDeviceObject folder, String name) {
		if (PortableDeviceStorageObject.class.isAssignableFrom(folder.getClass())) {
			PortableDeviceStorageObject f = (PortableDeviceStorageObject) folder;
			PortableDeviceObject[] objs = f.getChildObjects();
			for (PortableDeviceObject item : objs) {
				if (name.equals(item.getOriginalFileName())) {
					return item;
				}
			}
		} else if (PortableDeviceFolderObject.class.isAssignableFrom(folder.getClass())) {
			PortableDeviceFolderObject f = (PortableDeviceFolderObject) folder;
			PortableDeviceObject[] objs = f.getChildObjects();
			for (PortableDeviceObject item : objs) {
				if (name.equals(item.getOriginalFileName())) {
					return item;
				}
			}
		} else {
			return null;
		}
		return null;
	}

	static PortableDeviceObject findFile(PortableDeviceObject folder) {
		if (PortableDeviceStorageObject.class.isAssignableFrom(folder.getClass())) {
			PortableDeviceStorageObject f = (PortableDeviceStorageObject) folder;
			PortableDeviceObject[] objs = f.getChildObjects();
			for (PortableDeviceObject item : objs) {
				return item;
			}
		} else if (PortableDeviceFolderObject.class.isAssignableFrom(folder.getClass())) {
			PortableDeviceFolderObject f = (PortableDeviceFolderObject) folder;
			PortableDeviceObject[] objs = f.getChildObjects();
			for (PortableDeviceObject item : objs) {
				return item;
			}
		} else {
			return null;
		}
		return null;
	}

	static PortableDeviceObject[] getList(PortableDeviceObject folder) {
		if (PortableDeviceStorageObject.class.isAssignableFrom(folder.getClass())) {
			PortableDeviceStorageObject f = (PortableDeviceStorageObject) folder;
			PortableDeviceObject[] objs = f.getChildObjects();
			return objs;
		} else if (PortableDeviceFolderObject.class.isAssignableFrom(folder.getClass())) {
			PortableDeviceFolderObject f = (PortableDeviceFolderObject) folder;
			PortableDeviceObject[] objs = f.getChildObjects();
			return objs;
		} else {
			return null;
		}
	}

	static PortableDeviceToHostImpl32 host = new PortableDeviceToHostImpl32();

	public static boolean createDir(String destDirName) {
		File dir = new File(destDirName);
		if (dir.exists()) {// 判断目录是否存在
			return false;
		}
		if (!destDirName.endsWith(File.separator)) {// 结尾是否以"/"结束
			destDirName = destDirName + File.separator;
		}
		if (dir.mkdirs()) {// 创建目标目录
			return true;
		} else {
			return false;
		}
	}

	public static void copyFolder(PortableDeviceFolderObject folder, String target) {
		PortableDeviceObject[] objs = folder.getChildObjects();
		createDir(target);
		for (PortableDeviceObject item : objs) {

			if (PortableDeviceFolderObject.class.isAssignableFrom(item.getClass())) {
				copyFolder((PortableDeviceFolderObject) item, target + "\\" + item.getOriginalFileName());
			} else {
				try {
					System.out.println("\t" + item.getOriginalFileName());
					host.copyFromPortableDeviceToHost(item.getID(), target, device);
				} catch (COMException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		}
		if (move) {
			folder.delete(true);
		}
	}

	@JsonIgnoreProperties(ignoreUnknown = true)
    public static class Entry
    {
        public String title;
		public long owner_id;
        public long avid;
        public long total_bytes;
		public long downloaded_bytes;
		public long time_update_stamp;
        public Ep ep;
    	@JsonIgnoreProperties(ignoreUnknown = true)
        public static class Ep
        {
            public long av_id;
            public long episode_id;
        }
        public PageData page_data;
    	@JsonIgnoreProperties(ignoreUnknown = true)
        public static class PageData
        {
            public long cid;
        }
    }

	@JsonIgnoreProperties(ignoreUnknown = true)
    public static class Info
    {
        public String message;
        public Data data;
    	@JsonIgnoreProperties(ignoreUnknown = true)
        public static class Data
        {
            public long aid;
            public Owner owner;
        	@JsonIgnoreProperties(ignoreUnknown = true)
            public static class Owner
            {
                public long mid;
                public String name;
            }
        }
    }
	@SuppressWarnings("unchecked")
	public static <T> T deserialize1(String json, Class<T> clazz) {
		ObjectMapper objectMapper = new ObjectMapper();
		T object = null;
		try {
			object = (T) objectMapper.readValue(json, TypeFactory.rawClass(clazz));
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return object;
	}

	static char[] buff = new char[1024*1024];

	static <T> T readJson(String file, Class<T> clazz) throws IOException {
		FileInputStream fis = new FileInputStream(file);
		InputStreamReader isr = new InputStreamReader(fis, "utf-8");
		int len = isr.read(buff);
		if (len < 0) {
			System.out.print("error--------------------fdsa----------------------------");
		}
		String s = new String(buff, 0, len);
		T obj = deserialize1(s, clazz);

		isr.close();
		fis.close();

		return obj;
	}

	public static boolean deleteFile(String fileName) {
		File file = new File(fileName);
		// 如果文件路径所对应的文件存在，并且是一个文件，则直接删除
		if (file.exists() && file.isFile()) {
			if (file.delete()) {
				return true;
			} else {
				return false;
			}
		} else {
			return false;
		}
	}

	public static class Video {
		public String id;
		public String title;
		public long owner_id;
		public String owner;
		public long size;
		public long date;
	}

	public static void copy(int dev, String fn, String path) throws Exception {

		FileInputStream fis1 = new FileInputStream(fn);
		InputStreamReader isr1 = new InputStreamReader(fis1);
		char[] buff = new char[1024 * 512];
		int len = isr1.read(buff);
		String s = new String(buff, 0, len);
		isr1.close();
		fis1.close();

		HashSet<String> hs = new HashSet<String>();
		String[] ss = s.split("\r\n");
		for (String s1 : ss) {
			String id = s1.split(",")[0];
			hs.add(id);
		}

		PortableDeviceStorageObject root = getRoot(dev);
		PortableDeviceFolderObject bilibili = getBilibili(root);
		PortableDeviceObject[] objs = bilibili.getChildObjects();
		for (PortableDeviceObject item : objs) {
			if (!hs.contains(item.getOriginalFileName()))
				continue;
			System.out.println(item.getOriginalFileName());
			PortableDeviceFolderObject one = (PortableDeviceFolderObject) findFile(item);
			if (one == null)
				continue;

			PortableDeviceObject entry = (PortableDeviceObject) findFile(one, "entry.json");

			deleteFile("entry.json");
			try {
				host.copyFromPortableDeviceToHost(entry.getID(), ".", device);
			} catch (COMException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			Entry obj = readJson("entry.json", Entry.class);
			
			String name = obj.title;
			
			name = cleanName(name);
			
			String tmp = item.getOriginalFileName() + "_" + name;

			copyFolder((PortableDeviceFolderObject) item, path + "\\" + tmp);

		}

	}

}
